#include "StdAfx.h"
#include "XString.h"

namespace XGC
{
	static XGC_INLINE xgc_bool string_equal ( xgc_lpcstr v1, xgc_lpcstr v2 )
	{
		if( v1 == xgc_nullptr || v2 == xgc_nullptr )
			return false;

		xgc_lpcstr string1 = v1;
		xgc_lpcstr string2 = v2;

		return strcmp (string1, string2) < 0;
	}

	static XGC_INLINE xgc_uint32 string_hash( xgc_lpcstr v )
	{
		xgc_lpcstr p;
		xgc_uint32 h = 5381;

		for (p = v; *p != '\0'; p++)
			h = (h << 5) + h + *p;

		return h;
	}

	static XGC_INLINE xgc_size nearest_power ( xgc_size base, xgc_size num ) 
	{
		static const xgc_uint32 _max_size = std::numeric_limits< xgc_uint32 >::max();

		if (num > _max_size / 2)
		{
			return _max_size;
		}
		else
		{
			xgc_size n = base;

			while (n < num)
				n <<= 1;

			return n;
		}
	}

	xStaticStringPool::xStaticStringPool( xgc_size size )
		: ref_count( 0 )
	{
		xgc_size actual_size = nearest_power(1, size);

		const_table		= xgc_nullptr;
		storage_list	= xgc_nullptr;
		storage_next	= actual_size;
		storage_prev	= actual_size;
		default_size	= actual_size;
		this_size		= actual_size;
	}

	xStaticStringPool::~xStaticStringPool()
	{
		if (storage_list)
		{
			for( storage_list_t::iterator i = storage_list->begin(); i != storage_list->end(); ++i )
				free( *i );

			SAFE_DELETE( storage_list );
		}

		if (const_table)
			SAFE_DELETE( const_table );
	}

	xgc_lpstr xStaticStringPool::Insert( xgc_lpcstr str, xgc_int32 len, xgc_size *hash )
	{
		xgc_size size;
		xgc_lpstr pos;

		if ( len < 0)
			size = strlen (str);
		else
			size = len;

		if ((storage_next + size + sizeof(String)) > this_size)
		{
			xgc_size new_size = nearest_power (default_size, size + sizeof(String));

			if( storage_list == xgc_nullptr )
				storage_list = new storage_list_t();

			storage_list->push_front( (xgc_lpstr)malloc(new_size) );

			this_size = new_size;
			storage_next = 0;
			storage_prev = 0;
		}

		storage_list_t::iterator iter = storage_list->begin();
		pos = *iter + storage_next;

		String *pStr = (String *)pos;
		pStr->len = size;
		pStr->ref = 0;
		pStr->next = size + sizeof(String);
		pStr->prev = *iter + storage_prev - pos; // calculate relative position
		pStr->drop = false;

		*(pStr->str + pStr->len) = '\0';

		memcpy(pStr->str, str, pStr->len );

		storage_prev = storage_next;
		storage_next += size + sizeof(String);

		return pStr->str;
	}

	xgc_void xStaticStringPool::Clear()
	{
		if (storage_list)
		{
			for( storage_list_t::iterator i = storage_list->begin(); i != storage_list->end(); ++i )
				free( *i );

			SAFE_DELETE( storage_list );

			storage_list       = xgc_nullptr;
			storage_next       = default_size;
			this_size          = default_size;
		}

		if( const_table )
			const_table->clear();
	}

	xgc_lpstr xStaticStringPool::Insert( xgc_lpcstr str )
	{
		if( !const_table )
			const_table = new hash_table_t( key_compare( string_equal ) );

		hash_table_t::iterator i = const_table->find( str );
		if( i == const_table->end() )
		{
			xgc_lpstr lookup = Insert( str, -1, xgc_nullptr );
			hash_table_t::_Pairib ib = const_table->insert( hash_table_t::value_type( lookup, lookup ) );
			ASSERT( ib.second );

			return lookup;
		}

		return i->second;
	}

	XGC::xStaticString xStaticStringPool::DupString( xgc_lpcstr rval )
	{
		xgc_lpstr str = Insert( rval );
		if( str )
		{
			xStaticStringPool::String* pString = HeaderFromMember( xStaticStringPool::String, str, str );
			if( pString )
			{
				return xStaticString( this, pString->str );
			}
		}

		return xStaticString();
	}

	//////////////////////////////////////////////////////////////////////////
	// xString
	//////////////////////////////////////////////////////////////////////////
	xString& xString::assign ( const xString& rval )
	{
		/* Make sure assigning to itself doesn't corrupt the string.  */
		if(str != rval.str)
		{
			return assign( rval.c_str() );
		}

		return *this;
	}

	xString& xString::assign( xgc_lpcstr rval )
	{
		ASSERT_RETURN(rval != NULL, *this);

		/* Make sure assigning to itself doesn't corrupt the string.  */
		if(str != rval)
		{
			/* Assigning from substring should be ok since g_string_truncate
			does not realloc.  */
			truncate (0);
			append (rval);
		}

		return *this;
	}

	xgc_void xString::clear()
	{
		truncate( 0 );
	}

	void xString::expand( xgc_size length )
	{
		if (len + length >= allocated_len)
		{
			allocated_len = nearest_power (1, len + length + 1);
			xgc_lpstr _str = (xgc_lpstr)realloc (str, allocated_len);
			free( str );
			str = _str;

		}
	}

	xString& xString::insert( xgc_size pos, xgc_lpcstr val, xgc_size length /*= -1 */ )
	{
		ASSERT_RETURN(length == 0 || val != NULL, *this);

		if (length == 0)
			return *this;

		if (length < 0)
			len = strlen (val);

		if (pos < 0)
			pos = len;
		else
			ASSERT_RETURN (pos <= len, *this);

		/* Check whether val represents a substring of string.  This test
		probably violates chapter and verse of the C standards, since
		">=" and "<=" are only valid when val really is a substring.
		In practice, it will work on modern archs.  */
		if (val >= str && val <= str + len)
		{
			xgc_size offset = val - str;
			xgc_size precount = 0;

			expand (length);
			val = str + offset;
			/* At this point, val is valid again.  */

			/* Open up space where we are going to insert.  */
			if (pos < len)
				memmove (str + pos + length, str + pos, len - pos);

			/* Move the source part before the gap, if any.  */
			if (offset < pos)
			{
				precount = std::min(length, pos - offset);
				memcpy (str + pos, val, precount);
			}

			/* Move the source part after the gap, if any.  */
			if (len > precount)
				memcpy (str + pos + precount,
				val + /* Already moved: */ precount + /* Space opened up: */ length,
				length - precount);
		}
		else
		{
			expand (length);

			/* If we aren't appending at the end, move a hunk
			* of the old string to the end, opening up space
			*/
			if (pos < len)
				memmove (str + pos + length, str + pos, len - pos);

			/* insert the new string */
			if (length == 1)
				str[pos] = *val;
			else
				memcpy (str + pos, val, length);
		}

		len += length;

		str[len] = 0;

		return *this;
	}

	xString& xString::insert( xgc_size pos, xgc_char c )
	{
		expand (1);

		if (pos < 0)
			pos = len;
		else
			ASSERT_RETURN(pos <= len, *this);

		/* If not just an append, move the old stuff */
		if (pos < len)
			memmove (str + pos + 1, str + pos, len - pos);

		str[pos] = c;

		len += 1;

		str[len] = 0;

		return *this;
	}

	xString& xString::insert( xgc_size pos, xgc_wchar wc )
	{
		xgc_int32 charlen, first, i;
		xgc_lpstr dest;

		/* Code copied from g_unichar_to_utf() */
		if (wc < 0x80)
		{
			first = 0;
			charlen = 1;
		}
		else if (wc < 0x800)
		{
			first = 0xc0;
			charlen = 2;
		}
		else if (wc < 0x10000)
		{
			first = 0xe0;
			charlen = 3;
		}
		else if (wc < 0x200000)
		{
			first = 0xf0;
			charlen = 4;
		}
		else if (wc < 0x4000000)
		{
			first = 0xf8;
			charlen = 5;
		}
		else
		{
			first = 0xfc;
			charlen = 6;
		}
		/* End of copied code */

		expand (charlen);

		if (pos < 0)
			pos = len;
		else
			ASSERT_RETURN (pos <= len, *this);

		/* If not just an append, move the old stuff */
		if (pos < len)
			memmove (str + pos + charlen, str + pos, len - pos);

		dest = str + pos;
		/* Code copied from g_unichar_to_utf() */
		for (i = charlen - 1; i > 0; --i)
		{
			dest[i] = (wc & 0x3f) | 0x80;
			wc >>= 6;
		}
		dest[0] = wc | first;
		/* End of copied code */

		len += charlen;

		str[len] = 0;

		return *this;
	}

	xString& xString::overwrite( xgc_size pos, xgc_lpcstr val, xgc_size length /*= -1 */ )
	{
		xgc_size end;

		if (!length)
			return *this;

		ASSERT_RETURN (val != NULL, *this);
		ASSERT_RETURN (pos <= len, *this);

		if (length < 0)
			length = strlen (val);

		end = pos + length;

		if (end > len)
			expand (end - len);

		memcpy (str + pos, val, length);

		if (end > len)
		{
			str[end] = '\0';
			len = end;
		}

		return *this;
	}

	xString& xString::erase( xgc_size pos, xgc_size length )
	{
		ASSERT_RETURN (pos >= 0, *this);
		ASSERT_RETURN (pos <= length, *this);

		if (length < 0)
			length = len - pos;
		else
		{
			ASSERT_RETURN (pos + length <= len, *this);

			if (pos + length < len)
				memmove (str + pos, str + pos + length, len - (pos + length));
		}

		len -= length;

		str[len] = 0;

		return *this;
	}

	xStaticString::xStaticString()
		: pool( xgc_nullptr )
		, str( xgc_nullptr )
	{

	}

	xStaticString::xStaticString( const xStaticString& rval )
		: pool( xgc_nullptr )
		, str( xgc_nullptr )
	{
		assign( rval );
	}

	xStaticString::xStaticString( xStaticStringPool *_pool, xgc_lpstr _str )
		: pool( _pool )
		, str( _str )
	{
		pool->AddRef();
		if( _str )
		{
			xStaticStringPool::String *pString = HeaderFromMember( xStaticStringPool::String, _str, str );
			pString->AddRef();
		}
	}

	xgc_void xStaticString::assign( const xStaticString& rval )
	{
		if( rval.str == str )
			return;

		if( str )
		{
			xStaticStringPool::String *pString = HeaderFromMember( xStaticStringPool::String, str, str );
			pString->Release();
		}

		xStaticStringPool* old_pool = pool;
		str = rval.str;
		pool = rval.pool;
		
		if( str )
		{
			xStaticStringPool::String *pString = HeaderFromMember( xStaticStringPool::String, str, str );
			pString->AddRef();
		}

		if( pool )
			pool->AddRef();

		if( old_pool )
			old_pool->Release();
	}

	xgc_void xStaticString::clear()
	{
		if( str )
		{
			xStaticStringPool::String *pString = HeaderFromMember( xStaticStringPool::String, str, str );
			pString->Release();
		}

		if( pool )
			pool->Release();

		str = xgc_nullptr;
		pool = xgc_nullptr;
	}
}